#ifndef USER_DATABASE_H
#define USER_DATABASE_H

#include <time.h>
#include <tinyxml/tinyxml.h>

#include <user.h>
#include <http_connection.h>



class InvalidDatabaseResponse:public std::exception{
	public:
		InvalidDatabaseResponse(const std::string& s)throw():s(s){}
		~InvalidDatabaseResponse()throw(){}
		const char* what()throw(){
			return ("InvalidDatabaseResponse:"+s).c_str();
		}
	private:
		std::string s;
};
class UserAlreadyExists:public std::exception{
	public:
		UserAlreadyExists(const std::string& s)throw():s(s){}
		~UserAlreadyExists()throw(){}
		const char* what()throw(){
			return ("User already exists:"+s).c_str();
		}
	private:
		std::string s;
};
class UserAlreadyLoggedOn:public std::exception{
	public:
		UserAlreadyLoggedOn(const std::string& s)throw():s(s){}
		~UserAlreadyLoggedOn()throw(){}
		const char* what()throw(){
			return ("User already loggedon:"+s).c_str();
		}
	private:
		std::string s;
};
class UserDoesNotExist:public std::exception{
	public:
		UserDoesNotExist(const std::string& s)throw():s(s){}
		~UserDoesNotExist()throw(){}
		const char* what()throw(){
			return ("User does not exist:"+s).c_str();
		}
	private:
		std::string s;
};
class DatabaseError:public std::exception{
	public:
		DatabaseError(const std::string& s)throw():s(s){}
		~DatabaseError()throw(){}
		const char* what()throw(){
			return ("DatabaseError:"+s).c_str();
		}
	private:
		std::string s;
};
class UserDatabase{
	public:
		UserDatabase(const std::string& host){
			//craft HTTP command
			//splash_screen->setInfoText("Crafting HTTP command...");
			HttpConnection connection("naaaaak.com");
			
			std::map<std::string, std::string> get_variables;
			get_variables["action"]="syncCache";
			HttpRequest http_request("/~aaronds109/rpc.php", get_variables);
			
			
			//send message
			//splash_screen->setInfoText("Requesting Data...");
			connection<<http_request;
			g_debug("sent request");
			
			
			//get response
			//splash_screen->setInfoText("Waiting for response...");
			std::string http_response;
			connection>>http_response;
			
			//parse response into users
			TiXmlDocument document;
			document.Parse(http_response.c_str());
			TiXmlHandle docHandle( &document );
			
			TiXmlElement * message;
			TiXmlElement * response;
			if((message=document.FirstChildElement("message"))){
				if((response=message->FirstChildElement("response"))){
					if(std::string("success")==response->Attribute("value")){
						for(TiXmlElement* user_element=message->FirstChildElement("user");
							user_element; user_element=user_element->NextSiblingElement("user")){
							std::string email=user_element->Attribute("email");
							std::string username=user_element->Attribute("username");
							std::string password_hash=user_element->Attribute("password_hash");
							std::string buddies=user_element->Attribute("buddies");
							std::vector<std::string> buddy_list;
							std::stringstream ss(buddies);
							std::string buddy;
							while(getline(ss, buddy, ',')){
								std::cout<<"buddy:"<<buddy<<std::endl;
								buddy_list.push_back(buddy);
							}
							users.insert(std::make_pair(email, User(email, username, password_hash, buddy_list)));
					
						}
					}
					else{
						g_warning("UserDatabase::UserDatabase():value!=success");
					}
				}
				else{
					g_warning("UserDatabase::UserDatabase():response element not found");
				}
			}
			else{
				g_warning("UserDatabase::UserDatabase():message element not found");
			}
				
		}
		void addUser(const std::string& email, const std::string username, const std::string password_hash){
			HttpConnection connection("naaaaak.com");
			std::map<std::string, std::string> get_variables;
			get_variables["action"]="addUser";
			get_variables["email"]=email;
			get_variables["username"]=username;
			get_variables["password_hash"]=password_hash;
			
			//does user already exist?
			if(users.find(email)!=users.end()){
				throw UserAlreadyExists(email);
			}
			
			
			HttpRequest http_request("/~aaronds109/rpc.php", get_variables);
			
			//send message
			connection<<http_request;
			
			//get response
			std::string http_response;
			connection>>http_response;
			
			std::cout<<"addUser http response:"<<http_response<<std::endl;
			
			//parse response for success/fail
			TiXmlDocument document;
			document.Parse(http_response.c_str());
			TiXmlHandle docHandle( &document );
			
			TiXmlElement * message;
			TiXmlElement * response;
			if((message=document.FirstChildElement("message"))){
				if((response=message->FirstChildElement("response"))){
					if(std::string("success")==response->Attribute("value")){
						users.insert(std::make_pair(email, User(email, username, password_hash, std::vector<std::string>())));
					}
					else{
						g_warning("UserDatabase::adduser():value!=success");
					}
				}
				else{
					g_warning("UserDatabase::adduser():response element not found");
				}
			}
			else{
				g_warning("UserDatabase::adduser():message element not found");
			}
		}
		void removeUser(const std::string& email){
			HttpConnection connection("naaaaak.com");
			std::map<std::string, std::string> get_variables;
			get_variables["action"]="removeUser";
			get_variables["email"]=email;
			
			//does user exist?
			if(users.find(email)==users.end()){
				throw UserDoesNotExist(email);
			}
			
			
			HttpRequest http_request("/~aaronds109/rpc.php", get_variables);
			
			//send message
			connection<<http_request;
			
			//get response
			std::string http_response;
			connection>>http_response;
			
			//parse response for success/fail
			TiXmlDocument document;
			document.Parse(http_response.c_str());
			TiXmlHandle docHandle( &document );
			
			TiXmlElement * message;
			TiXmlElement * response;
			if((message=document.FirstChildElement("message"))){
				if((response=message->FirstChildElement("response"))){
					if(std::string("success")==response->Attribute("value")){
						users.erase(email);
					}
					else{
						g_warning("UserDatabase::removeUser():value!=success");
					}
				}
				else{
					g_warning("UserDatabase::removeuser():response element not found");
				}
			}
			else{
				g_warning("UserDatabase::removeUser():message element not found");
			}
		}
		void addBuddy(const std::string& email, const std::string& buddy_email){
			HttpConnection connection("naaaaak.com");
			std::map<std::string, std::string> get_variables;
			get_variables["action"]="addBuddy";
			get_variables["email"]=email;
			get_variables["buddy_email"]=buddy_email;
			
			
			//does user exist?
			if(users.find(email)==users.end()){
				throw UserDoesNotExist(email);
			}
			//does buddy exist?
			if(users.find(buddy_email)==users.end()){
				throw UserDoesNotExist(buddy_email);
			}
			
			
			HttpRequest http_request("/~aaronds109/rpc.php", get_variables);
			
			//send message
			connection<<http_request;
			
			//get response
			std::string http_response;
			connection>>http_response;
			
			//parse response for success/fail
			TiXmlDocument document;
			document.Parse(http_response.c_str());
			TiXmlHandle docHandle( &document );
			
			TiXmlElement * message;
			TiXmlElement * response;
			if((message=document.FirstChildElement("message"))){
				if((response=message->FirstChildElement("response"))){
					if(std::string("success")==response->Attribute("value")){
						std::map<std::string, User>::iterator itr=users.find(email);
						if(itr!=users.end()){
							itr->second.addBuddy(buddy_email);
						}
					}
					else{
						g_warning("UserDatabase::addBuddy():value!=success");
					}
				}
				else{
					g_warning("UserDatabase::addBuddy():response element not found");
				}
			}
			else{
				g_warning("UserDatabase::addBuddy():message element not found");
			}
		}
		void removeBuddy(const std::string& email, const std::string& buddy_email){
			HttpConnection connection("naaaaak.com");
			std::map<std::string, std::string> get_variables;
			get_variables["action"]="removeBuddy";
			get_variables["email"]=email;
			get_variables["buddy_email"]=buddy_email;
			
			
			//does user exist?
			if(users.find(email)==users.end()){
				throw UserDoesNotExist(email);
			}
			//does buddy exist?
			if(users.find(buddy_email)==users.end()){
				throw UserDoesNotExist(buddy_email);
			}
			
			HttpRequest http_request("/~aaronds109/rpc.php", get_variables);
			
			//send message
			connection<<http_request;
			
			//get response
			std::string http_response;
			connection>>http_response;
			
			//parse response for success/fail
			TiXmlDocument document;
			document.Parse(http_response.c_str());
			
			g_debug("UserDatabase::removeBuddy():database response parse correctly");
			
			TiXmlElement * message;
			TiXmlElement * response;
			if((message=document.FirstChildElement("message"))){
				g_debug("Found message element");
				if((response=message->FirstChildElement("response"))){
					g_debug("Found response element");
					if(std::string("success")==response->Attribute("value")){
						g_debug("value==success");
						std::map<std::string, User>::iterator itr=users.find(email);
						if(itr!=users.end()){
							itr->second.removeBuddy(buddy_email);
						}
					}
					else{
						g_warning("UserDatabase::removeBuddy():value!=success");
					}
				}
				else{
					g_warning("UserDatabase::removeBuddy():response element not found");
				}
			}
			else{
				g_warning("UserDatabase::removeBuddy():message element not found");
			}
		}
		void setUserOnline(const std::string& email, const std::string& password_hash){			
			std::map<std::string, User>::iterator itr=users.find(email);
			if(itr!=users.end()){
				itr->second.login(password_hash);
			}
			else{
				throw UserDoesNotExist(email);
			}
		}
		void setUserOffline(const std::string& email){
			std::map<std::string, User>::iterator itr=users.find(email);
			if(itr!=users.end()){
				itr->second.logout();
			}
			else{
				throw UserDoesNotExist(email);
			}
		}
		
		std::map<std::string, std::string> getBuddyStatus(const std::string& email){
			std::map<std::string, std::string> result;
			std::map<std::string, User>::iterator itr=users.find(email);
			if(itr!=users.end()){
				//sync current user's time
				itr->second.syncTime();
				
				//get list of buddy's and status
				for(std::vector<std::string>::const_iterator buddy_itr=itr->second.getBuddies().begin();
					buddy_itr!=itr->second.getBuddies().end(); ++buddy_itr){
					if(itr->second.isOnline()){
						result.insert(std::make_pair(*buddy_itr, "online"));
					}
					else{
						result.insert(std::make_pair(*buddy_itr, "offline"));
					}						
				}
			}
			else{
				throw UserDoesNotExist(email);
			}
			return result;
		}
		User getUser(const std::string& email)const{
			std::map<std::string, User>::const_iterator itr=users.find(email);
			if(itr!=users.end()){
				return itr->second;
			}
			else{
				throw UserDoesNotExist(email);
			}
		}
		User& getUser(const std::string& email){
			std::map<std::string, User>::iterator itr=users.find(email);
			if(itr!=users.end()){
				return itr->second;
			}
			else{
				throw UserDoesNotExist(email);
			}
		}
		std::vector<const User*> getUsers()const{
			std::vector<const User*> result;
			std::map<std::string, User>::const_iterator itr=users.begin();
			for(;itr!=users.end(); ++itr){
				result.push_back(&itr->second);
			}
			return result;
		}
	private:
		//HttpConnection connection;
		std::map<std::string, User> users;
};

#endif
